package com.mentorgen.tools.util.profile.bundle;

import java.io.File;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
import org.osgi.framework.ServiceReference;
import org.osgi.service.packageadmin.PackageAdmin;

public class BundleContainer {
	
	protected Bundle bundle = null;
	protected BundleContext context;
	protected BundleType    type;
	
	String bundleLoc;
	
	public enum BundleType {INSTRUMENTED, ORIGINAL};
	
	private static String instBundleRoot = null;
	
	public BundleContainer(BundleContext _context, Bundle _bundle){
		
		this.bundle = _bundle;
		this.context = _context;
		
		instBundleRoot = context.getProperty(OSGiServiceProfiler.INST_BUNDLE_ROOT);

		this.bundleLoc = bundle.getLocation();
		this.type = (BundleContainer.isInstrumented(bundle) ? BundleType.INSTRUMENTED : 
			BundleType.ORIGINAL);
	}
	
	public void swap() throws BundleException, URISyntaxException, InterruptedException{
		
		if (this.bundle == null || bundle.getState() != Bundle.ACTIVE)
			return;
		
//		String bundleInstLoc = this.getAltLocation();  
		this.stop();
		this.uninstall();
		this.install(this.getAltLocation());
		
		this.type = (this.type == BundleType.ORIGINAL) ? BundleType.INSTRUMENTED : BundleType.ORIGINAL;
		System.out.println("Swapped " + bundle.getSymbolicName() + " bundle to: " + 
				((this.type == BundleType.ORIGINAL) ? "ORIGINAL" : "INSTRUMENTED"));
	}
	
	public void swap(boolean b) throws BundleException, URISyntaxException, InterruptedException {
		
		System.err.println("Especial Swapping Begins");
		
		Set<Bundle> involvedBundleSet = new HashSet<Bundle>();
		
		if (this.bundle == null || bundle.getState() != Bundle.ACTIVE)
			return;
		
		ServiceReference[] srefs = bundle.getRegisteredServices();
		ServiceReference[] usingRefs = bundle.getServicesInUse();
		
		if (srefs != null)
			for (ServiceReference sref : srefs){
				Bundle[] bundles = sref.getUsingBundles();
				
				if (bundles != null)
					involvedBundleSet.addAll (Arrays.asList(bundles));
			}
		
		if (usingRefs != null)
			for (ServiceReference usingRef : usingRefs){
				Bundle bundle = usingRef.getBundle();
				if (bundle != null)
					involvedBundleSet.add(bundle);
			}
		
		this.stop();
		this.uninstall();

        // Get package admin service.
        ServiceReference ref = context.getServiceReference(
            org.osgi.service.packageadmin.PackageAdmin.class.getName());
        if (ref == null)
        {
            System.out.println("PackageAdmin service is unavailable.");
            return;
        }

        PackageAdmin pa = (PackageAdmin) context.getService(ref);
        if (pa == null)
        {
        	System.out.println("PackageAdmin service is unavailable.");
            return;
        }

        pa.refreshPackages((involvedBundleSet.size() == 0)
            ? null
            : (Bundle[]) involvedBundleSet.toArray(new Bundle[involvedBundleSet.size()]));
        
        this.install(this.getAltLocation());
        
        this.type = (this.type == BundleType.ORIGINAL) ? BundleType.INSTRUMENTED : BundleType.ORIGINAL;
        
        for (Bundle involvedBundle : involvedBundleSet){
        	if (involvedBundle.getState() != Bundle.ACTIVE)
        		involvedBundle.start();
        }
		System.err.println("Especial Swapping is Done!");
	}
	
	public boolean isBundleNull(){
		return (this.bundle == null ? true : false);
	}
	
	private void install(String bundleLocation) throws BundleException, InterruptedException {
		
			this.bundle = context.installBundle(bundleLocation);
			this.bundle.start();
//			Thread.currentThread().sleep(10);
	}
	
	private void stop() throws BundleException {
		
			this.bundle.stop();
	}
	
	private void uninstall() throws BundleException {
		
		if (this.bundle != null)
			this.bundle.uninstall();
	}
	
	private String getAltLocation() throws URISyntaxException {
		
		if (this.type != BundleType.ORIGINAL)
			return bundleLoc;
		
		String bundleInstRoot = !context.getProperty(OSGiServiceProfiler.INST_BUNDLE_ROOT).endsWith(System.getProperty("file.separator")) ?
				context.getProperty(OSGiServiceProfiler.INST_BUNDLE_ROOT) + System.getProperty("file.separator") :
				context.getProperty(OSGiServiceProfiler.INST_BUNDLE_ROOT);
		
		return OSGiServiceProfiler.FILE_INSTALLATION_EXT + 
				new File(bundleInstRoot + bundle.getSymbolicName() + OSGiServiceProfiler.JAR_EXTENSION).getAbsolutePath();
	}
	
	public static boolean isInstrumented(Bundle bundle){
		return bundle.getLocation().contains(instBundleRoot) ?
				true : false;
	}

	public String getBundleSymbolicName(){
		return this.bundle.getSymbolicName();
	}
	
	public BundleType getBundleType(){
		return this.type;
	}
}
